package media

import (
	"context"
	"errors"
	"os"
	"server/common"
	"sync"
	"time"

	"github.com/jiyeyuran/mediasoup-go/v2"
)

type WorkerManager struct {
	workers map[int]*mediasoup.Worker
	mtx     sync.RWMutex
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func NewWorkerManager() *WorkerManager {
	return &WorkerManager{
		workers: make(map[int]*mediasoup.Worker),
	}
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *WorkerManager) Init(config *Config) error {
	workerBin := os.Getenv("MEDIASOUP_WORKER_BIN")
	if workerBin == "" {
		return errors.New("MEDIASOUP_WORKER_BIN environment variable is not set")
	}

	for i := 0; i < config.Mediasoup.NumWorkers; i++ {
		worker, err := mediasoup.NewWorker(workerBin, func(options *mediasoup.WorkerSettings) {
			*options = *config.Mediasoup.WorkerSettings
		})
		if err != nil {
			panic(err)
		}

		worker.OnClose(func(ctx context.Context) {
			common.Logger(common.SERVICE_TYPE_MEDIA).Info("mediasoup Worker closed")
		})

		s.mtx.Lock()
		s.workers[worker.Pid()] = worker
		s.mtx.Unlock()

		go func() {
			ticker := time.NewTicker(120 * time.Second)
			for {
				select {
				case <-ticker.C:
					usage, err := worker.GetResourceUsage()
					if err != nil {
						common.Logger(common.SERVICE_TYPE_MEDIA).Error(
							"mediasoup Worker getResourceUsage", "pid", worker.Pid(), "error", err)
						continue
					}
					common.Logger(common.SERVICE_TYPE_MEDIA).Info(
						"mediasoup Worker resource usage", "pid", worker.Pid(), "usage", usage)
				}
			}
		}()
	}
	return nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *WorkerManager) GetWorker(pid int) (*mediasoup.Worker, error) {
	s.mtx.RLock()
	defer s.mtx.RUnlock()
	worker, exists := s.workers[pid]
	if !exists {
		return nil, errors.New("worker not found")
	}
	return worker, nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *WorkerManager) GetWorkers() []int {
	s.mtx.RLock()
	defer s.mtx.RUnlock()
	var workers []int
	for pid := range s.workers {
		workers = append(workers, pid)
	}
	return workers
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *WorkerManager) WorkerExists(pid int) bool {
	s.mtx.RLock()
	defer s.mtx.RUnlock()
	_, exists := s.workers[pid]
	return exists
}
